package com.guli.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.common.util.UuidUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guli.common.constant.MqConstant;
import com.guli.common.constant.OrderConstant;
import com.guli.common.to.SeckillOrderTo;
import com.guli.common.utils.PageUtils;
import com.guli.common.utils.Query;
import com.guli.common.utils.R;
import com.guli.common.vo.CouponVo;
import com.guli.common.vo.CouponsAvailableVo;
import com.guli.common.vo.MemberRespVo;
import com.guli.common.constant.LuaFile;
import com.guli.order.dao.OrderDao;
import com.guli.order.entity.OrderEntity;
import com.guli.order.entity.OrderItemEntity;
import com.guli.order.entity.PaymentInfoEntity;
import com.guli.common.enume.OrderStatusEnum;
import com.guli.order.feign.*;
import com.guli.order.interceptor.LoginUserInterceptor;
import com.guli.order.service.OrderItemService;
import com.guli.order.service.OrderService;
import com.guli.order.service.PaymentInfoService;
import com.guli.order.to.OrderCreateTo;
import com.guli.order.to.SpuInfoTo;
import com.guli.order.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    private final ThreadLocal<OrderSubmitVo> orderSubmitVoThreadLocal = new ThreadLocal<>();
    @Autowired
    OrderItemService orderItemService;
    @Autowired
    MemberFeignService memberFeignService;
    @Autowired
    CartFeignService cartFeignService;
    @Autowired
    ThreadPoolExecutor executor;
    @Autowired
    WmsFeignService wmsFeignService;
    @Autowired
    ProductFeignService productFeignService;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    PaymentInfoService paymentInfoService;
    @Autowired
    private CouponFeignService couponFeignService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() {
        //创建订单确认对象
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        //获取当前登录用户
        MemberRespVo memberRespVo = LoginUserInterceptor.threadLocal.get();
        //返回当前绑定到线程的 RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //异步查询收货地址
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            //获取该用户地址
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            confirmVo.setMemberAddressVos(address);
        }, executor);
        //异步查询购物车数据
        CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
            //购物车数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<OrderItemVo> items = cartFeignService.getCurrentUserCartItems();
            confirmVo.setItems(items);
        }, executor).thenRunAsync(() -> {
            //查询购物车的数据是否具有库存
            List<OrderItemVo> orderItemVos = confirmVo.getItems();
            List<Long> skuIds = orderItemVos.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
            R result = wmsFeignService.hasStocks(skuIds);
            Map<String, Boolean> hasStock = result.getData(new TypeReference<Map<String, Boolean>>() {
            });
            confirmVo.setStocks(hasStock);
            orderItemVos.forEach(orderItem -> {
                R res1 = productFeignService.getWeight(orderItem.getSkuId());
                BigDecimal weight = res1.getData(new TypeReference<BigDecimal>() {
                });
                orderItem.setWeight(weight);
            });
            //查询可使用的优惠卷
            R res2 = couponFeignService.getAvailableCoupons(orderItemVos);
            if (res2.getCode() != 0) {
                log.error("远程调用失败");
            }
            List<CouponsAvailableVo> availableVos = res2.getData(new TypeReference<List<CouponsAvailableVo>>() {
            });
            confirmVo.setCouponsAvailableVo(availableVos);
        }, executor);
        //生成防重令牌
        String token = UuidUtils.generateUuid().replace("-", "");
        //redis保存token
        redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN + memberRespVo.getId(), token);
        //设置防刷临牌
        confirmVo.setOrderToken(token);
        //计算商品数量
        int count = 0;
        List<OrderItemVo> orderItemVos = confirmVo.getItems();
        for (OrderItemVo orderItemVo : orderItemVos) {
            count += orderItemVo.getCount();
        }
        confirmVo.setCount(count);
        //计算订单总额
        BigDecimal totalPirce = BigDecimal.valueOf(0);
        BigDecimal payPrice = BigDecimal.valueOf(0);
        for (OrderItemVo orderItemVo : orderItemVos) {
            totalPirce = totalPirce.add(orderItemVo.getTotalPirce());
            payPrice = payPrice.add(orderItemVo.getTotalPirce());
        }
        confirmVo.setTotal(totalPirce);
        //根据chooseCouponId查询优惠卷
        //优惠卷可以叠加
        List<Long> chooseCouponIds = confirmVo.getChooseCouponId();
        if (chooseCouponIds != null && chooseCouponIds.size() > 0) {
            R couponInfo = couponFeignService.getCouponsByIds(chooseCouponIds);
            List<CouponVo> couponVos = couponInfo.getData(new TypeReference<List<CouponVo>>() {
            });
            //获取优惠卷总金额
            BigDecimal subtractAmount = BigDecimal.ZERO;
            if (!CollectionUtils.isEmpty(couponVos)) {
                for (CouponVo couponVo : couponVos) {
                    subtractAmount = subtractAmount.add(couponVo.getAmount());
                }
            }
            //计算应付价格
            payPrice = payPrice.subtract(subtractAmount);
            confirmVo.setPayPrice(payPrice);
        }
        try {
            CompletableFuture.allOf(addressFuture, cartFuture).get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return confirmVo;
    }


    /**
     * 用户信息，订单信息，商品信息，物流信息，支付信息，促销信息
     * @param orderSubmitVo
     * @return
     */
    /*@GlobalTransactional*/
    @Transactional
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        //设置线程变量
        orderSubmitVoThreadLocal.set(orderSubmitVo);
        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        //获取当前登录的用户
        MemberRespVo memberRespVo = LoginUserInterceptor.threadLocal.get();
        responseVo.setCode(0);
        //验证令牌
        String orderToken = orderSubmitVo.getOrderToken();
        //令牌的对比和删除必须保证原子性
        Long success = redisTemplate.execute
                (new DefaultRedisScript<>(LuaFile.lua, Long.class),
                        Collections.singletonList(OrderConstant.ORDER_TOKEN + memberRespVo.getId()),
                        orderToken);
        if (success == 0L) {
            //令牌验证不通过
            responseVo.setCode(1);
            return responseVo;
        }
        //令牌校验通过
        //创建订单，验令牌，验价格，所库存
        //获取使用的优惠劵
        List<Long> chooseCouponIds = orderSubmitVo.getChooseCouponIds();
        OrderCreateTo orderTo = this.createOrder(chooseCouponIds);
        //获取当前计算后实际应付金额
        BigDecimal payAmount = orderTo.getOrder().getPayAmount();
        //获取提交过来的订单支付金额
        BigDecimal payPrice = orderSubmitVo.getPayPrice();
        if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {
            //金额对比
            //保存订单
            boolean saveOrder = this.saveOrder(orderTo);
            if (!saveOrder) {
                return null;
            }
            //  库存锁定  只要有异常回滚订单数据
            //  订单号，所有订单项(skuId,skuName,num)
            WareSkuLockVo lockVo = new WareSkuLockVo();
            //设置订单ID
            lockVo.setOrderId(orderTo.getOrder().getId());
            //设置订单号
            lockVo.setOrderSn(orderTo.getOrder().getOrderSn());
            //设置收货人
            lockVo.setConsignee(orderTo.getOrder().getReceiverName());
            //设置收货人电话
            lockVo.setConsigneeTel(orderTo.getOrder().getReceiverName());
            //设置收货配送地址
            lockVo.setDeliveryAddress(orderTo.getOrder().getReceiverDetailAddress());
            //设置付款方式
            lockVo.setPaymentWay(1);
            //设置创建时间
            lockVo.setCreateTime(new Date());
            List<OrderItemVo> orderItemVos = orderTo.getOrderItems()
                    .stream().map(orderItem -> {
                        OrderItemVo orderItemVo = new OrderItemVo();
                        orderItemVo.setSkuId(orderItem.getSkuId());
                        orderItemVo.setCount(orderItem.getSkuQuantity());
                        orderItemVo.setTitle(orderItem.getSkuName());
                        return orderItemVo;
                    }).collect(Collectors.toList());
            lockVo.setLocks(orderItemVos);
            //远程锁库存
            R lockStock = wmsFeignService.orderLockStock(lockVo);
            if (lockStock.getCode() == 0) {
                //库存锁定成功
                responseVo.setOrder(orderTo.getOrder());
                //订单创建成功发送消息给MQ
                rabbitTemplate.convertAndSend(
                        MqConstant.ORDER_EVENT_EXCHANGE,
                        MqConstant.ORDER_CREATE_ROUTING,
                        orderTo.getOrder()
                );
            } else {
                //库存锁定失败
                responseVo.setCode(3);
            }
        } else {
            //价格校验失败
            responseVo.setCode(2);
        }
        return responseVo;
    }

    @Override
    public R getOrderStatusByOrderSn(String orderSn) {
        OrderEntity orderEntity = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return R.ok().put("data", orderEntity);
    }

    @Override
    public void closeOrder(OrderEntity orderEntity) {
        //查询订单状态
        OrderEntity order = this.getById(orderEntity.getId());
        if (order != null) {
            Integer status = order.getStatus();
            if (Objects.equals(status, OrderStatusEnum.CREATE_NEW.getCode())) {
                //订单状态为未付款 关闭订单
                order.setStatus(OrderStatusEnum.CANCLED.getCode());
                this.updateById(order);
                rabbitTemplate.convertAndSend(MqConstant.ORDER_EVENT_EXCHANGE, MqConstant.STOCK_RELEASE_ROUTING, order);
            }
        }
    }

    @Override
    public PayVo getOrderPay(String orderSn) {
        PayVo payVo = new PayVo();
        R order = this.getOrderStatusByOrderSn(orderSn);
        OrderEntity orderEntity = JSON.parseObject(JSON.toJSONString(order.get("data")), OrderEntity.class);
        BigDecimal totalPrice = orderEntity.getPayAmount().setScale(2, RoundingMode.UP);
        //设置总金额
        payVo.setTotal_amount(totalPrice.toString());
        //设置商户订单号
        payVo.setOut_trade_no(orderSn);
        //设置订单名称
        payVo.setSubject("谷粒商城收银");
        //设置商品描述
        payVo.setBody("谷粒商城收银");
        //返回
        return payVo;
    }

    @Override
    public PageUtils listWithItem(Map<String, Object> params) {
        MemberRespVo memberRespVo = LoginUserInterceptor.threadLocal.get();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().eq("member_id", memberRespVo.getId())
                        .orderByAsc("id")
        );
        List<OrderEntity> orders = page.getRecords().stream().map(order -> {
            List<OrderItemEntity> itemEntities = orderItemService.list(
                    new QueryWrapper<OrderItemEntity>().eq("order_sn", order.getOrderSn()));
            order.setOrderItemEntityList(itemEntities);
            return order;
        }).collect(Collectors.toList());
        page.setRecords(orders);
        return new PageUtils(page);
    }

    @Override
    public String handlePayResult(PayAsyncVo payAsyncVo) {
        PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
        paymentInfoEntity.setAlipayTradeNo(payAsyncVo.getTrade_no());
        paymentInfoEntity.setOrderSn(payAsyncVo.getOut_trade_no());
        paymentInfoEntity.setPaymentStatus(payAsyncVo.getTrade_status());
        paymentInfoEntity.setCallbackTime(payAsyncVo.getNotify_time());
        paymentInfoService.save(paymentInfoEntity);
        //修改订单的状态信息
        if (payAsyncVo.getTrade_status().equals("TRADE_SUCCESS") || payAsyncVo.getTrade_status().equals("TRADE_FINISHED")) {
            //支付成功状态
            String outTradeNo = payAsyncVo.getOut_trade_no();
            this.baseMapper.updateOrderStatus(outTradeNo, OrderStatusEnum.PAYED.getCode());
        }
        return "success";
    }

    @Override
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setMemberId(seckillOrderTo.getMemberId());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        BigDecimal price = seckillOrderTo.getSeckillPrice().multiply(new BigDecimal("" + seckillOrderTo.getNum()));
        orderEntity.setPayAmount(price);
        this.save(orderEntity);
        //保存订单项信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderItemEntity.setRealAmount(price);
        orderItemEntity.setSkuQuantity(seckillOrderTo.getNum());
        orderItemService.save(orderItemEntity);
    }

    /*
     * 保存订单
     * */
    private boolean saveOrder(OrderCreateTo order) {
        MemberRespVo memberRespVo = LoginUserInterceptor.threadLocal.get();
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        orderEntity.setMemberId(memberRespVo.getId());
        //保存订单
        boolean save = this.save(orderEntity);
        List<OrderItemEntity> orderItems = order.getOrderItems();
        //保存订单项
        boolean saveBatch = orderItemService.saveBatch(orderItems);
        if (save && saveBatch) {
            log.info("订单保存成功，用户Id: {} ，订单号为: {}", memberRespVo.getId(), orderEntity.getOrderSn());
            return true;
        }
        log.info("订单保存失败，用户Id: {} ，订单号为: {}", memberRespVo.getId(), orderEntity.getOrderSn());
        return false;
    }

    /**
     * 生成订单
     * (1)、获取商品信息
     * (2)、获取优惠信息
     * (3)、会员权益及积分兑换信息
     * (4)、锁定库存
     * (5)、运费计算
     * (6)、生成订单
     * @return
     */
    private OrderCreateTo createOrder(List<Long> chooseCouponIds) {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        //生成订单号
        String orderSn = IdWorker.getTimeId();
        // 构建订单 构建订单项 根据收货地址计算运费 构建优惠卷信息
        OrderEntity orderEntity = this.buildOrder(orderSn);
        //构建每个订单项
        List<OrderItemEntity> itemEntities = buildOrderItems(orderSn);
        //验价
        computePrice(orderEntity, itemEntities, chooseCouponIds);
        orderCreateTo.setOrder(orderEntity);
        orderCreateTo.setOrderItems(itemEntities);
        return orderCreateTo;
    }

    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> itemEntities, List<Long> chooseCouponIds) {
        //设置实际总额为0
        BigDecimal totalPrice = new BigDecimal("0.0");
        //优惠卷优惠金额
        BigDecimal couponAmount = new BigDecimal("0.0");
        //商品促销优惠金额
        BigDecimal promotionAmount = new BigDecimal("0.0");
        BigDecimal realAmount = new BigDecimal("0.0");
        for (OrderItemEntity itemEntity : itemEntities) {
            realAmount = realAmount.add(itemEntity.getRealAmount());
            promotionAmount = promotionAmount.add(itemEntity.getPromotionAmount());
        }
        totalPrice = realAmount.subtract(promotionAmount);
        //实际订单总额
        orderEntity.setTotalAmount(totalPrice);
        //查询可用优惠卷
        R couponInfos = couponFeignService.getCouponsByIds(chooseCouponIds);
        List<CouponVo> couponVos = couponInfos.getData(new TypeReference<List<CouponVo>>() {
        });
        //查询优惠总额
        if (!CollectionUtils.isEmpty(couponVos)) {
            List<BigDecimal> couponPrices = couponVos.stream().map(CouponVo::getAmount).collect(Collectors.toList());
            for (BigDecimal couponPrice : couponPrices) {
                couponAmount = couponAmount.add(couponPrice);
            }
        }
        //应付金额
        BigDecimal payAmount = totalPrice.add(orderEntity.getFreightAmount()).subtract(couponAmount);
        //设置实际应付金额
        orderEntity.setPayAmount(payAmount);
    }

    private List<OrderItemEntity> buildOrderItems(String orderSn) {
        //获取用户当前购物车
        List<OrderItemVo> currentUserOrderItems = cartFeignService.getCurrentUserCartItems();
        List<OrderItemEntity> itemEntities = null;
        if (currentUserOrderItems != null && currentUserOrderItems.size() > 0) {
            //构建每个订单项并收集成集合
            itemEntities = currentUserOrderItems.stream().map(orderItem -> this.buildOrderItem(orderItem, orderSn))
                    .collect(Collectors.toList());
        }
        return itemEntities;
    }

    /**
     * 构建订单的收货地址
     * 构建订单的收货地址计算运费
     * @param orderSn
     * @return
     */
    private OrderEntity buildOrder(String orderSn) {
        OrderEntity orderEntity = new OrderEntity();
        //设置订单号
        orderEntity.setOrderSn(orderSn);
        //从ThreadLocal中获取当前订单
        OrderSubmitVo submitVo = orderSubmitVoThreadLocal.get();
        //根据收货地址ID查询费用
        R fare = wmsFeignService.getFare(submitVo.getAddrId());
        FareVo fareVo = fare.getData(new TypeReference<FareVo>() {
        });
        //设置运费信息
        orderEntity.setFreightAmount(fareVo.getFare());
        //设置收货城市
        orderEntity.setReceiverCity(fareVo.getMemberAddressVo().getCity());
        //设置收货人姓名
        orderEntity.setReceiverName(fareVo.getMemberAddressVo().getName());
        //设置收货人手机号
        orderEntity.setReceiverPhone(fareVo.getMemberAddressVo().getPhone());
        //设置收货人邮政编码
        orderEntity.setReceiverPostCode(fareVo.getMemberAddressVo().getPostCode());
        //设置收货省份
        orderEntity.setReceiverProvince(fareVo.getMemberAddressVo().getProvince());
        //设置收货地区
        orderEntity.setReceiverRegion(fareVo.getMemberAddressVo().getRegion());
        //设置收货详细地区
        orderEntity.setReceiverDetailAddress(fareVo.getMemberAddressVo().getDetailAddress());
        //设置订单状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        //设置删除状态
        orderEntity.setDeleteStatus(0);
        //返回
        return orderEntity;
    }

    /**
     * 构建每一个订单项
     * @param orderItemVo
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo orderItemVo, String orderSn) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        CompletableFuture<Void> productFuture = CompletableFuture.runAsync(() -> {
            // 商品的SPU信息
            R spuInfo = productFeignService.getSpuInfoBySkuId(orderItemVo.getSkuId());
            SpuInfoTo spuInfoTo = spuInfo.getData(new TypeReference<SpuInfoTo>() {
            });
            //商品的SPU信息
            orderItemEntity.setSpuId(spuInfoTo.getId());
            orderItemEntity.setSpuBrand(spuInfoTo.getBrandName());
            orderItemEntity.setSpuName(spuInfoTo.getSpuName());
            orderItemEntity.setCategoryId(spuInfoTo.getCatalogId());
            //商品的SKU信息
            orderItemEntity.setSkuId(orderItemVo.getSkuId());
            orderItemEntity.setSkuName(orderItemVo.getTitle());
            orderItemEntity.setSkuPic(orderItemVo.getImage());
            orderItemEntity.setSkuPrice(orderItemVo.getPrice());
            orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(orderItemVo.getSkuAttr(), ";"));
            orderItemEntity.setSkuQuantity(orderItemVo.getCount());
        });
        CompletableFuture<Void> promotionFuture = CompletableFuture.runAsync(() -> {
            // 查询商品促销分解金额 默认设置为0
            orderItemEntity.setPromotionAmount(BigDecimal.valueOf(0));
            // 优惠券优惠分解金额
            orderItemEntity.setCouponAmount(BigDecimal.valueOf(0));
            // 设置赠送成长值
            orderItemEntity.setGiftGrowth(orderItemVo.getPrice().intValue());
            // 设置赠送积分
            orderItemEntity.setGiftIntegration(orderItemVo.getPrice().intValue());
            // 当前订单项的实际金额
            BigDecimal realTotal = orderItemEntity.getSkuPrice()
                    .multiply(new BigDecimal(orderItemEntity.getSkuQuantity()))
                    .subtract(orderItemEntity.getPromotionAmount());
            orderItemEntity.setOrderSn(orderSn);
            orderItemEntity.setRealAmount(realTotal);
        });
        try {
            //等待productFuture、promotionFuture运行完
            CompletableFuture.allOf(productFuture, promotionFuture).get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
        return orderItemEntity;
    }
}